/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.spring.web;

import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import org.springframework.web.context.request.WebRequestInterceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.DispatcherServletWebRequest;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * Adapter for WebRequestInterceptor to be converted or act as a HandlerInterceptor.
 *
 * @author <a href="mailto:malonso@silenus-consultoria.es">Mariano Alonso</a>
 *
 * @since 29-mar-2009 12:56:01
 */
public class WebRequestAdapterInterceptor implements HandlerInterceptor {
	/**
	 * The path matcher.
	 */
	private PathMatcher pathMatcher;

	/**
	 * The paths.
	 */
	private List<String> paths;

	/**
	 * The adaptee.
	 */
	private WebRequestInterceptor interceptor;

/**
   * Constructor.
   */
	public WebRequestAdapterInterceptor() {
		pathMatcher = new AntPathMatcher();
	}

	/**
	 * Sets the path matcher.
	 *
	 * @param pathMatcher the path matcher.
	 */
	public void setPathMatcher(final PathMatcher pathMatcher) {
		this.pathMatcher = pathMatcher;
	}

	/**
	 * Sets the paths where interception will occur.
	 *
	 * @param paths the paths.
	 */
	public void setPaths(final List<String> paths) {
		this.paths = paths;
	}

	/**
	 * Sets the adaptee interceptor.
	 *
	 * @param interceptor the adaptee interceptor.
	 */
	public void setInterceptor(final WebRequestInterceptor interceptor) {
		this.interceptor = interceptor;
	}

	/**
	 * Checks if the request matches.
	 *
	 * @param request the request.
	 *
	 * @return true if matches, false otherwise.
	 */
	private boolean matches(final HttpServletRequest request) {
		if(paths != null) {
			String path = request.getRequestURI();
			String contextPath = request.getContextPath();

			final String subPath = path.substring(contextPath.length());

			for(String p : paths) {
				if(pathMatcher.match(p, subPath)) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Intercept the execution of a handler. Called after HandlerMapping determined an appropriate handler object,
	 * but before HandlerAdapter invokes the handler.<p>DispatcherServlet processes a handler in an execution
	 * chain, consisting of any number of interceptors, with the handler itself at the end. With this method, each
	 * interceptor can decide to abort the execution chain, typically sending a HTTP error or writing a custom response.</p>
	 *
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler chosen handler to execute, for type and/or instance evaluation
	 *
	 * @return <code>true</code> if the execution chain should proceed with the next interceptor or the handler itself.
	 * 				 Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself.
	 *
	 * @throws Exception in case of errors
	 */
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
		if(matches(request)) {
			interceptor.preHandle(new DispatcherServletWebRequest(request));
		}

		return true;
	}

	/**
	 * Intercept the execution of a handler. Called after HandlerAdapter actually invoked the handler, but before
	 * the DispatcherServlet renders the view. Can expose additional model objects to the view via the given
	 * ModelAndView.<p>DispatcherServlet processes a handler in an execution chain, consisting of any number of
	 * interceptors, with the handler itself at the end. With this method, each interceptor can post-process an
	 * execution, getting applied in inverse order of the execution chain.</p>
	 *
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler chosen handler to execute, for type and/or instance examination
	 * @param modelAndView the <code>ModelAndView</code> that the handler returned (can also be <code>null</code>)
	 *
	 * @throws Exception in case of errors
	 */
	public void postHandle(
		HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
		throws Exception {
		if(matches(request)) {
			interceptor.postHandle(
				new DispatcherServletWebRequest(request), ((modelAndView != null) ? modelAndView.getModelMap() : null));
		}
	}

	/**
	 * Callback after completion of request processing, that is, after rendering the view. Will be called on any
	 * outcome of handler execution, thus allows for proper resource cleanup.<p>Note: Will only be called if this
	 * interceptor's <code>preHandle</code> method has successfully completed and returned <code>true</code>!</p>
	 *
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler chosen handler to execute, for type and/or instance examination
	 * @param ex exception thrown on handler execution, if any
	 *
	 * @throws Exception in case of errors
	 */
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
		throws Exception {
		if(matches(request)) {
			interceptor.afterCompletion(new DispatcherServletWebRequest(request), ex);
		}
	}
}
